import * as IPFS from 'ipfs'
import OrbitDB from 'orbit-db'
import 'colors'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'

import { 字串化, 组化, 编码字串 } from './_util.js'


let ipfsNode, 节点ID, orbitdb
const store = {}
async function 初始化() {
  const ipfsOptions = { repo: './orbitdb/海问助手/ipfs', EXPERIMENTAL: { pubsub: true } }
  try {
    ipfsNode = await IPFS.create(ipfsOptions)
    ipfsNode.libp2p.addEventListener('peer:connect', 连接同行后)
  } catch (e) {
    console.error(e.message)
    if (e.name === 'LockExistsError') console.error('⛔ 数据库被锁住了，请重新执行！'.red.bold)
    process.exit(1)
  }
  节点ID = (await ipfsNode.id()).id.toString()

  orbitdb = await OrbitDB.createInstance(ipfsNode, { directory: './orbitdb/海问助手' })
  const dbInstOptions = { accessController: { write: ['*'] } }
  const docStoreOptions = { ...dbInstOptions, indexBy: 'hash' }

  store.我 = await orbitdb.keyvalue('我', dbInstOptions)
  await store.我.load()
  store.册子 = await orbitdb.docstore('册子', docStoreOptions)
  await store.册子.load()
  const 设定的个人资料 = { ...Object.fromEntries('我 册子'.split(' ').map(类目 => [`${类目}ID`, store[类目].id])), 节点ID }
  await 载入设定个人资料(设定的个人资料)

  store.通讯录 = await orbitdb.keyvalue('通讯录', dbInstOptions)
  await store.通讯录.load()
  await ipfsNode.pubsub.subscribe(节点ID, 收到消息时)

  console.log('\n✔ 数据库启动完成'.green)
  const 节库描述 = '本机节点及数据库信息：'
  store.各库 = Object.values(store).filter(_ => _.address).map(_ => `type:${_.type};${_.address.toString()}`)
  console.log(`${节库描述}${store.各库.join(`\n${' '.repeat(2 * 节库描述.length)}`)}`)

  globalThis.res({ 节点ID, orbitdb, store }) // 放在作用域的最后
}
初始化()
export default new Promise(_ => globalThis.res = _)


export function 连接同行(multiAddr) {
  return ipfsNode.swarm.connect(multiAddr)
}
export function 连接同行后(e) {
  console.log(e.detail)
  知会同行(同行.id, { 意图: '复制数据库', 数据: store.各库 })
}

export async function 收到消息时(消息) {
  const { 意图, 数据 } = JSON.parse(消息.data.toString())
  switch (意图) {
    case '复制数据库': 复制数据库(数据); break
  }
}

export function 复制数据库(库描述) {
  组化(库描述).forEach(async 描述 => {
    const { type, addr } = 描述.match(/type:(?<type>.+?);(?<addr>.+)/).groups
    const 库 = await orbitdb.open(addr, { create: true, type })
    库.events.on('replicated', async () => {
      库.get('册子') && await store.通讯录.set(库.id, 库.all)
    })
  })
}

export async function 知会同行(同行ID, 消息对象, 回调) {
  await ipfsNode.pubsub.publish(同行ID/* ——残废之处，尼玛明明就是 PeerID 它却死不认 */, uint8ArrayFromString(字串化(消息对象)))
}

export async function 连接全部同行() {
  const 在线的对等点 = await ipfsNode.swarm.peers()
  Object.values(store.通讯录.all).forEach(async 联系人 => {
    const { 节点ID } = 联系人
    if (!在线的对等点.includes(节点ID)) return
    await 连接同行(节点ID)
  })
}

async function 载入设定个人资料(资料) {
  for (const [键, 值] of Object.entries(资料)) !store.我.get(键) && await store.我.set(键, 值)
}